package ws

import (
	"github.com/gorilla/websocket"
	"log"
	"sync"
	"time"
)

var (
	pongWait             = 10 * time.Second
	writeWait            = 10 * time.Second
	pingInterval         = (pongWait * 9) / 10
	maxMessageSize int64 = 1024
)

type Client struct {
	sync.RWMutex
	Cid        int
	manager    *Manager
	Conn       *websocket.Conn
	ReadChan   chan *MsgPack
	WriteChan  chan []byte
	Session    *Session
	pingTicker *time.Ticker
}

func NewClient(connect *websocket.Conn, manager *Manager) *Client {
	return &Client{
		manager:    manager,
		Conn:       connect,
		ReadChan:   manager.ClientReadChan,
		WriteChan:  make(chan []byte, 1024),
		pingTicker: nil,
	}
}

func (c *Client) Run() {
	go c.Write()
	go c.Read()
}
func (c *Client) Read() {
	defer func() {
		c.manager.removeClient(c)
	}()
	c.Conn.SetReadLimit(maxMessageSize)
	c.Conn.SetReadDeadline(time.Now().Add(pongWait))
	c.Conn.SetPongHandler(func(data string) error {
		log.Println("receive pong", data)
		c.Conn.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})
	for {
		messageType, message, err := c.Conn.ReadMessage()
		log.Println(messageType, string(message))
		if err != nil {
			break
		}
		//客户端发来的消息是二进制消息
		if messageType == websocket.TextMessage {
			if c.ReadChan != nil {
				c.ReadChan <- &MsgPack{
					Cid:  c.Cid,
					Body: message,
				}
			}
		}
	}
}
func (c *Client) Write() {
	defer func() {
		c.pingTicker.Stop()
		c.manager.removeClient(c)
	}()
	c.pingTicker = time.NewTicker(pingInterval)
	for {
		select {
		case <-c.pingTicker.C:
			c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
			err := c.Conn.WriteMessage(websocket.PingMessage, nil)
			if err != nil {
				log.Println("ping error:", err)
				return
			}
		case message, ok := <-c.WriteChan:
			c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
			if !ok {
				c.Conn.WriteMessage(websocket.CloseMessage, nil)
				return
			}
			err := c.Conn.WriteMessage(websocket.TextMessage, message)
			if err != nil {
				return
			}
		}
	}
}

func (c *Client) GetSession() *Session {
	return c.Session
}

func (c *Client) SendMessage(buf []byte) error {
	c.WriteChan <- buf
	return nil
}

func (c *Client) Close() {
	if c.Conn != nil {
		c.Conn.Close()
	}
	if c.pingTicker != nil {
		c.pingTicker.Stop()
	}
}
